package a5.grammar.productions;

import java.util.LinkedList;
import java.util.List;

import a5.Interpreter;
import a5.common.ProgramCursor;
import a5.common.OrderedTree;
import a5.common.Token;
import a5.environment.Environment;
import a5.grammar.annotations.Grammar;
import a5.grammar.grammars.G1;
import a5.grammar.interfaces.Production;

/**
 * <pre>
 * This class provides a method for interpreting the following production
 * FunctionName -> UserFunctionName | + | -
 * 
 * Simply, at the point of execution at which {@link #interpret(Interpreter)} is invoked, 
 * a function name should be present (either UserFunctioName or built in). 
 * 
 * This production uses recursive decent to interpret and parse.
 * </pre>
 * 
 * @author Jason Campos
 * @version Assignment 5, CS 152 (Programming Paradigms) Fall 2012, Jeffrey Smith
 */
@Grammar(clazz=G1.class)
public class FunctionName implements Production{

	// Hide constructor
	private FunctionName(){}
	
	/**
	 * Interprets the program being examined by the argument interpreter 
	 * using the defined rules for this production. 
	 * 
	 * This method slightly bends the rules of the production as it does not read 
	 * from left to right. By checking the current point of execution for one of the 
	 * {@link a5.environment.Environment Environment's} 
	 * prior to checking user defined function names, constant functions are prevented from
	 * being overwritten by user defined functions of the same name. Note however that this 
	 * is the case iff the Environment's {@link #getConstantFunctions() getConstantFunctions()}
	 * method obeys its contract to return the functions created at instantiation.
	 * 
	 * @param environment
	 * The {@link a5.environment.Environment Environment} responsible for variable and function maintenance
	 * and scoping. 
	 * 
	 * @param cursor
	 * The {@link a5.common.ProgramCursor ProgramCursor} which contains the {@link a5.common.Token Tokens} 
	 * of the program being interpreted.
	 * 
	 * @return
	 * The name of the {@link a5.function.Function Function} at the current point of execution.
	 */
	public String interpret(Environment environment, ProgramCursor cursor) {
		// First, look through environments built-in functions.
		// This allows environment to have different build ins without hard-coding the "match" for each built-in.
		for(String functionName : environment.getConstantFunctions().keySet())
			if(cursor.first(functionName))
				return cursor.match(environment, functionName);
		
		// If this is a user defined function, return the value of the text
		return cursor.match(environment, "UserFunctionName");
	}
	
	/**
	 * Recursively parses the program of the argument cursor using the variable/function scoping
	 * policies of the argument environment.
	 * 
	 * @param cursor
	 * The {@link a5.common.ProgramCursor ProgramCursor} which contains the {@link a5.common.Token Tokens} 
	 * of the program being interpreted.
	 * 
	 * @return
	 * An {@link a5.common.OrderedTree OrderedTree<Token>} representing the parse structure of the 
	 * the program of the argument cursor.
	 */
	public OrderedTree<Token> parse(ProgramCursor cursor){
		List<OrderedTree<Token>> children = new LinkedList<OrderedTree<Token>>();
		
		if(cursor.first("UserFunctionName"))
			children.add(new OrderedTree<Token>(new Token(cursor.match("UserFunctionName"))));
		else if (cursor.first("+"))
			children.add(new OrderedTree<Token>(new Token(cursor.match("+"))));
		else
			// Peek at the token type to determine which element to add to the parse tree
			children.add(new OrderedTree<Token>(new Token(cursor.match("-"))));
		
		return new OrderedTree<Token>(new Token(this.getClass().getSimpleName()), children);
	}
	
	/**
	 * @param cursor
	 * The {@link a5.common.ProgramCursor ProgramCursor} which contains the {@link a5.common.Token Tokens} 
	 * of the program being interpreted.
	 * 
	 * @return
	 * {@code true} if the execution point of the program is at a point which indicates that
	 * an FunctionName is a valid path of execution. In other words, if the current point of
	 * execution contains an element which is the first element of this production 
	 * ("UserFunctionName", "+", "-").
	 */
	public boolean first(ProgramCursor cursor) {
		return cursor.first("UserFunctionName") || cursor.first("+") || cursor.first("-");
	}
}
